package com.corticon.dialogs.web.designer;

import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.support.SessionStatus;

import com.corticon.dialogs.dao.UserDAO;
import com.corticon.dialogs.model.User;
import com.corticon.dialogs.service.PortalUserService;
import com.corticon.dialogs.validation.UserValidator;
import com.corticon.dialogs.validation.ValidationMessageFormatter;
import com.corticon.dialogs.web.BaseController;

/**
 * Spring MVC Controller for portal user sign in.
 * Cteate a new user, save it in database.
 * Make the user login automatically after successfully creating.
 *
 * @author Minghui Zheng
 */
@Controller
public class UserSignInController extends BaseController {

    @Resource
    private UserDAO userDAO;
    @Resource
    private PortalUserService portalUserService;
    /**
     * A instance of inner class CreateUserValidator for properties validation of User.
     */
    private CreateUserValidator createUserValidator;
    //Returned view
    private static final String SIGNIN_VIEW = "designer.general.signin";

    /**
     * Instantiate the validators after the construct method.
     * This method will be auto invoked.
     */
    @PostConstruct
    private void buildValidators() {
        createUserValidator = new CreateUserValidator(validationMessageFormatter);
    }

    /**
     * Redirect to sign in page, so user can input info for create a new account.
     *
     * @return View of sign in page.
     */
    @RequestMapping(value = "signin", method = RequestMethod.GET)
    public String entrySignIn(HttpServletRequest request, ModelMap model) {
        User user = new User();
        model.addAttribute("user", user);
        request.getSession().setAttribute("LAST_USERNAME_KEY", "");
        return SIGNIN_VIEW;
    }

    /**
     * This method is controller for handling user registration request.
     *
     * @param user
     * @param errors
     * @param sessionStatus
     * @param request
     * @return "redirect:/index.jsp"
     */
    @RequestMapping(value = "signin", method = RequestMethod.POST)
    public String signIn(@ModelAttribute("user") User user, Errors errors,
                         SessionStatus sessionStatus,
                         HttpServletRequest request) {

        this.doValidate(user, errors, request);

        if (errors.hasErrors()) {
            return SIGNIN_VIEW;
        }

        portalUserService.signInUser(user, request);

        sessionStatus.setComplete();
        request.getSession().setAttribute("LAST_USERNAME_KEY", user.getUsername());

        //redirect to main page.
        return this.redirectPrefix + "/designer/questionnaire/list.html";
    }

    private void doValidate(User user, Errors errors, HttpServletRequest request) {
        //Validate the form object.
        Map<String, List<String>> errorsMessages = createUserValidator.invokeValidate(user, errors,
                request.getLocale());

        //Put error messages into request.
        request.setAttribute("errors", errorsMessages);
    }

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    public void setPortalUserService(PortalUserService portalUserService) {
        this.portalUserService = portalUserService;
    }
// ********** Inner Classes ********** //

    /**
     * The validator for the user create.
     */
    private class CreateUserValidator extends UserValidator<User> {

        public CreateUserValidator(ValidationMessageFormatter validationMessageFormatter) {
            super.validationMessageFormatter = validationMessageFormatter;
        }

        @Override
        public void complexValidate(Object object, Errors errors) {

            super.complexValidate(object, errors);

            // The type of the object has been checked in super class, so this cast is type safe.
            User user = (User) object;

            // Validate whether the username is already existed.
            User oldUser = userDAO.findById(user.getUsername());
            if (oldUser != null) {
                errors.rejectValue("username", "validate.user.username.duplicate",
                        "This username has been used; please select a username between 4 and 12 characters consisting of alpha and numeric characters.");
            }
            usernameValidate(user, errors);
        }

        /**
         * Validate whether the username involves invalid characters.
         *
         * @param user
         * @param errors
         */
        private void usernameValidate(User user, Errors errors) {
            if (!checkInvalidCharacters(user.getUsername(), ' ', '/', null) || !checkInvalidCharacters(
                    user.getUsername(), ':', '@', null) || !checkInvalidCharacters(user.getUsername(),
                    '[', '`', null) || !checkInvalidCharacters(user.getUsername(), '{', '~', null)) {

                errors.rejectValue("username", "validate.user.username.invalid",
                        "This username has been used; please select a username between 4 and 12 characters consisting of alpha and numeric characters.");
            }
        }
    }
}